import 'package:flutter/cupertino.dart';
import 'package:petitparser/petitparser.dart';
import '../common/test_page.dart';
import 'utils/matchers.dart';

class ParsetTestPage extends TestPage {
  ParsetTestPage(String title, {Key? key}) : super(title, key: key) {
    group('action', () {
      group('cast', () {
        test('default', () {
          final parser = digit().map(int.parse).cast<num>();
          expect(parser, null);
        });
      });
      group('castList', () {
        test('default', () {
          final parser = digit().map(int.parse).repeat(3).castList<num>();
          expect(parser, null);
        });
      });
      group('callCC', () {
        test('delegation', () {
          final parser = digit().callCC((continuation, context) => continuation(context));
          expect(parser, null);
        });
        test('diversion', () {
          final parser = digit().callCC((continuation, context) => letter().parseOn(context));
          expect(parser, null);
        });
        test('resume', () {
          final continuations = <ContinuationFunction>[];
          final contexts = <Context>[];
          final parser = digit().callCC((continuation, context) {
            continuations.add(continuation);
            contexts.add(context);
            // we have to return something for now
            return context.failure('Abort');
          });
          // execute the parser twice to collect the continuations
          expect(parser.parse('1').isSuccess, null); //isFalse
          expect(parser.parse('a').isSuccess, null); //isFalse
          // later we can execute the captured continuations
          expect(continuations[0](contexts[0]).isSuccess, null); //isTrue
          expect(continuations[1](contexts[1]).isSuccess, null); //isFalse
          // of course the continuations can be resumed multiple times
          expect(continuations[0](contexts[0]).isSuccess, null); //isTrue
          expect(continuations[1](contexts[1]).isSuccess, null); //isFalse
        });
        test('success', () {
          final parser = digit().callCC((continuation, context) => context.success('success'));
          expect(parser, null);
        });
        test('failure', () {
          final parser = digit().callCC((continuation, context) => context.failure('failure'));
          expect(parser, null);
        });
      });
      group('flatten', () {
        // expectParserInvariants(any().flatten());
        test('default', () {
          final parser = digit().repeat(2, unbounded).flatten();
          expect(parser, null);
        });
        test('with message', () {
          final parser = digit().repeat(2, unbounded).flatten('gimme a number');
          expect(parser, null);
        });
      });
      group('where', () {
        test('default', () {
          final parser = any().where((value) => value == '*');
          expect(parser, null);
        });
        test('with failure message', () {
          final parser = digit()
              .plus()
              .flatten()
              .map(int.parse)
              .where((value) => value % 7 == 0, failureMessage: (value) => '$value is not divisible by 7');
          expect(parser, null);
        });
        test('with failure position', () {
          final inner = any() & any();
          final parser = inner.where((value) => value[0] == value[1], failurePosition: (tokens) => 1);
          expect(parser, null);
        });
        test('with failure message and position', () {
          final inner = any() & any();
          final parser = inner.where((list) => list[0] == list[1],
              failureMessage: (list) => '${list[0]} != ${list[1]}', failurePosition: (list) => 1);
          expect(parser, null);
        });
      });
      group('map', () {
        test('default', () {
          final parser = digit().map((each) => each.codeUnitAt(0) - '0'.codeUnitAt(0));
          expect(parser, null);
        });
      });
      group('permute', () {
        test('from start', () {
          final parser = digit().seq(letter()).permute([1, 0]);
          expect(parser, null);
        });
        test('from end', () {
          final parser = digit().seq(letter()).permute([-1, 0]);
          expect(parser, null);
        });
        test('repeated', () {
          final parser = digit().seq(letter()).permute([1, 1]);
          expect(parser, null);
        });
      });
      group('pick', () {
        test('from start', () {
          final parser = digit().seq(letter()).pick(1);
          expect(parser, null);
        });
        test('from end', () {
          final parser = digit().seq(letter()).pick(-1);
          expect(parser, null);
        });
      });
      group('token', () {
        test('default', () {
          final parser = digit().plus().token();
          expect(parser, null);
          final token = parser.parse('123').value;
          expect(token.value, ['1', '2', '3']);
          expect(token.buffer, '123');
          expect(token.start, 0);
          expect(token.stop, 3);
          expect(token.input, '123');
          expect(token.length, 3);
          expect(token.line, 1);
          expect(token.column, 1);
          expect(token.toString(), 'Token[1:1]: [1, 2, 3]');
        });
        const buffer = '1\r12\r\n123\n1234';
        final parser = any().map((value) => value.codeUnitAt(0)).token().star();
        final result = parser.parse(buffer).value;
        test('value', () {
          final expected = [49, 13, 49, 50, 13, 10, 49, 50, 51, 10, 49, 50, 51, 52];
          expect(result.map((token) => token.value), expected);
        });
        test('buffer', () {
          final expected = List.filled(buffer.length, buffer);
          expect(result.map((token) => token.buffer), expected);
        });
        test('start', () {
          final expected = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
          expect(result.map((token) => token.start), expected);
        });
        test('stop', () {
          final expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
          expect(result.map((token) => token.stop), expected);
        });
        test('length', () {
          final expected = List.filled(buffer.length, 1);
          expect(result.map((token) => token.length), expected);
        });
        test('line', () {
          final expected = [1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4];
          expect(result.map((token) => token.line), expected);
        });
        test('column', () {
          final expected = [1, 2, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4];
          expect(result.map((token) => token.column), expected);
        });
        test('input', () {
          final expected = ['1', '\r', '1', '2', '\r', '\n', '1', '2', '3', '\n', '1', '2', '3', '4'];
          expect(result.map((token) => token.input), expected);
        });
        test('map', () {
          final expected = ['49', '13', '49', '50', '13', '10', '49', '50', '51', '10', '49', '50', '51', '52'];
          expect(result.map((token) => token.map((value) => value.toString())).map((token) => token.value), expected);
        });
        group('join', () {
          test('normal', () {
            final joined = Token.join(result);
            expect(joined, null);
          });
          test('reverse order', () {
            final joined = Token.join(result.reversed);
            expect(joined, null);
          });
          test('empty, 此处应该为X', () {
            Token.join([]);
            // expect(() => Token.join([]), throwsArgumentError);
          });
          test('different buffer, 此处应该为X', () {
            const token = [Token(12, '12', 0, 2), Token(32, '32', 0, 2)];
            Token.join(token);
            // expect(() => Token.join(token), throwsArgumentError);
          });
        });
        test('unique', () {
          expect({...result}.length, result.length);
        });
        test('equals', () {
          for (var i = 0; i < result.length; i++) {
            for (var j = 0; j < result.length; j++) {
              final condition = i == j ? true : false;
              expect(result[i] == result[j], condition);
              expect(result[i].hashCode == result[j].hashCode, condition);
            }
          }
        });
      });
      group('trim', () {
        test('default', () {
          final parser = char('a').trim();
          expect(parser, null);
        });
        test('custom both', () {
          final parser = char('a').trim(char('*'));
          expect(parser, null);
        });
        test('custom left and right', () {
          final parser = char('a').trim(char('*'), char('#'));
          expect(parser, null);
        });
      });
    });
    group('character', () {
      group('anyOf', () {
        final parser = anyOf('uncopyrightable');
        test('default', () {
          expect(parser, null);
        });
      });
      group('noneOf', () {
        final parser = noneOf('uncopyrightable');
        test('default', () {
          expect(parser, null);
        });
      });
      group('char', () {
        test('with string', () {
          final parser = char('a');
          expect(parser, null);
        });
        test('with message', () {
          final parser = char('a', 'lowercase a');
          expect(parser, null);
        });
        test('char invalid, 此处应该为X', () {
          char('ab');
          // expect(() => char('ab'), throwsArgumentError);
        });
        <String, String>{
          '\\x00': '\x00',
          '\\b': '\b',
          '\\t': '\t',
          '\\n': '\n',
          '\\v': '\v',
          '\\f': '\f',
          '\\r': '\r',
          '\\"': '"',
          "\\'": "'",
          '\\\\': '\\',
          '☠': '\u2620',
          ' ': ' ',
        }.forEach((key, value) {
          test('char("$key")', () {
            final parser = char(value);
            expect(parser, null);
          });
        });
      });
      group('charIgnoringCase', () {
        test('with lowercase string', () {
          final parser = charIgnoringCase('a');
          expect(parser, null);
        });
        test('with uppercase string', () {
          final parser = charIgnoringCase('A');
          expect(parser, null);
        });
        test('with custom message', () {
          final parser = charIgnoringCase('a', 'upper or lower');
          expect(parser, null);
        });
        test('with single char', () {
          final parser = charIgnoringCase('1');
          expect(parser, null);
        });
        test('char invalid, 此处应该为X', () {
          charIgnoringCase('ab');
          // expect(() => charIgnoringCase('ab'), throwsArgumentError);
        });
      });
      group('digit', () {
        final parser = digit();
        // expectParserInvariants(parser);
        test('default', () {
          expect(parser, null);
        });
      });
      group('letter', () {
        final parser = letter();
        test('default', () {
          expect(parser, null);
        });
      });
      group('lowercase', () {
        final parser = lowercase();
        test('default', () {
          expect(parser, null);
        });
      });
      group('pattern', () {
        test('with single', () {
          final parser = pattern('abc');
          expect(parser, null);
        });
        test('with range', () {
          final parser = pattern('a-c');
          expect(parser, null);
        });
        test('with overlapping range', () {
          final parser = pattern('b-da-c');
          expect(parser, null);
        });
        test('with adjacent range', () {
          final parser = pattern('c-ea-c');
          expect(parser, null);
        });
        test('with prefix range', () {
          final parser = pattern('a-ea-c');
          expect(parser, null);
        });
        test('with postfix range', () {
          final parser = pattern('a-ec-e');
          expect(parser, null);
        });
        test('with repeated range', () {
          final parser = pattern('a-ea-e');
          expect(parser, null);
        });
        test('with composed range', () {
          final parser = pattern('ac-df-');
          expect(parser, null);
        });
        test('with negated single', () {
          final parser = pattern('^a');
          expect(parser, null);
        });
        test('with negated range', () {
          final parser = pattern('^a-c');
          expect(parser, null);
        });
        test('with negate but without range', () {
          final parser = pattern('^a-');
          expect(parser, null);
        });
        test('with error, 此处应该为X', () {
          pattern('c-a');
          // expect(() => pattern('c-a'), throwsArgumentError);
        });
        group('ignore case', () {
          // expectParserInvariants(patternIgnoreCase('^ad-f'));
          test('with single', () {
            final parser = patternIgnoreCase('abc');
            expect(parser, null);
          });
          test('with range', () {
            final parser = patternIgnoreCase('a-c');
            expect(parser, null);
          });
          test('with overlapping range', () {
            final parser = patternIgnoreCase('b-da-c');
            expect(parser, null);
          });
          test('with adjacent range', () {
            final parser = patternIgnoreCase('c-ea-c');
            expect(parser, null);
          });
          test('with prefix range', () {
            final parser = patternIgnoreCase('a-ea-c');
            expect(parser, null);
          });
          test('with postfix range', () {
            final parser = patternIgnoreCase('a-ec-e');
            expect(parser, null);
          });
          test('with repeated range', () {
            final parser = patternIgnoreCase('a-ea-e');
            expect(parser, null);
          });
          test('with composed range', () {
            final parser = patternIgnoreCase('ac-df-');
            expect(parser, null);
          });
          test('with negated single', () {
            final parser = patternIgnoreCase('^a');
            expect(parser, null);
          });
          test('with negated range', () {
            final parser = patternIgnoreCase('^a-c');
            expect(parser, null);
          });
          test('with negate but without range', () {
            final parser = patternIgnoreCase('^a-');
            expect(parser, null);
          });
          test('with error， 此处应该为X', () {
            patternIgnoreCase('c-a');
            // expect(() => patternIgnoreCase('c-a'), throwsArgumentError);
          });
        });
        group('large ranges', () {
          final parser = pattern('\u2200-\u22ff\u27c0-\u27ef\u2980-\u29ff');
          test('mathematical symbols', () {
            expect(parser, null);
          });
        });
        group('without anything', () {
          final parser = pattern('');
          test('test', () {
            for (var i = 0; i <= 0xffff; i++) {
              final character = String.fromCharCode(i);
              expect(parser, null);
            }
          });
        });
        group('with everything', () {
          final parser = pattern('\x00-\uffff');
          test('test', () {
            for (var i = 0; i <= 0xffff; i++) {
              final character = String.fromCharCode(i);
              expect(parser, null);
            }
          });
        });
      });
      group('range', () {
        final parser = range('e', 'o');
        test('default', () {
          expect(parser, null);
        });
        test('invalid， 此处应该为X', () {
          range('o', 'e');
          // expect(() => range('o', 'e'), throwsArgumentError);
        });
      });
      group('uppercase', () {
        final parser = uppercase();
        test('default', () {
          expect(parser, null);
        });
      });
      group('whitespace', () {
        final parser = whitespace();
        test('default', () {
          expect(parser, null);
        });
        test('unicode', () {
          final whitespace = {
            9,
            10,
            11,
            12,
            13,
            32,
            133,
            160,
            5760,
            8192,
            8193,
            8194,
            8195,
            8196,
            8197,
            8198,
            8199,
            8200,
            8201,
            8202,
            8232,
            8233,
            8239,
            8287,
            12288,
            65279
          };
          for (var i = 0; i < 65536; i++) {
            var character = String.fromCharCode(i);
            expect(parser, null);
          }
        });
      });
      group('word', () {
        final parser = word();
        test('default', () {
          expect(parser, null);
        });
      });
    });
    group('combinator', () {
      group('and', () {
        test('default', () {
          final parser = char('a').and();
          expect(parser, null);
        });
      });
      group('choice', () {
        test('operator', () {
          final parser = char('a') | char('b');
          expect(parser, null);
        });
        test('converter', () {
          final parser = [char('a'), char('b')].toChoiceParser();
          expect(parser, null);
        });
        test('two', () {
          final parser = char('a').or(char('b'));
          expect(parser, null);
        });
        test('three', () {
          final parser = char('a').or(char('b')).or(char('c'));
          expect(parser, isParseSuccess(parser, 'a'));
        });
        test('empty, 此处应该为X', () {
          <Parser>[].toChoiceParser();
          // expect(() => <Parser>[].toChoiceParser(), throwsArgumentError);
        });
        group('types', () {
          test('same', () {
            final first = any();
            final second = any();
            expect(first is Parser<String>, null);

            expect(second is Parser<String>, null);

            expect(ChoiceParser([first, second]) is Parser<String>, null);

            expect([first, second].toChoiceParser() is Parser<String>, null);
          });
          test('ChoiceParser(Iterable<Parser<R>> children, {FailureJoiner? failureJoiner})', () {
            final first = any().map(int.parse);
            final second = any().map(double.parse);
            expect(first is Parser<int>, null);

            expect(second is Parser<double>, null);

            expect(ChoiceParser([first, second]) is Parser<num>, null);

            expect([first, second].toChoiceParser() is Parser<num>, null);
          });
        });
        group('failure joining', () {
          const failureA0 = Failure('A0', 0, 'A0');
          const failureA1 = Failure('A1', 1, 'A1');
          const failureB0 = Failure('B0', 0, 'B0');
          const failureB1 = Failure('B1', 1, 'B1');
          final parsers = [
            anyOf('ab').plus() & anyOf('12').plus(),
            anyOf('ac').plus() & anyOf('13').plus(),
            anyOf('ad').plus() & anyOf('14').plus(),
          ].map((parser) => parser.flatten());
          test('construction', () {
            final defaultTwo = any().or(any());
            expect(defaultTwo.failureJoiner(failureA1, failureA0), failureA0);
            final customTwo = any().or(any(), failureJoiner: selectFarthest);
            expect(customTwo.failureJoiner(failureA1, failureA0), failureA1);
            final customCopy = customTwo.copy();
            expect(customCopy.failureJoiner(failureA1, failureA0), failureA1);
            final customThree = any().or(any(), failureJoiner: selectFarthest).or(any());
            expect(customThree.failureJoiner(failureA1, failureA0), failureA1);
          });
          test('select first', () {
            final parser = parsers.toChoiceParser(failureJoiner: selectFirst);
            expect(selectFirst(failureA0, failureB0), failureA0);
            expect(selectFirst(failureB0, failureA0), failureB0);
            expect(parser, null);
          });
          test('select last', () {
            final parser = parsers.toChoiceParser(failureJoiner: selectLast);
            expect(selectLast(failureA0, failureB0), failureB0);
            expect(selectLast(failureB0, failureA0), failureA0);
            expect(parser, null);
          });
          test('farthest failure', () {
            final parser = parsers.toChoiceParser(failureJoiner: selectFarthest);
            expect(selectFarthest(failureA0, failureB0), failureB0);
            expect(selectFarthest(failureA0, failureB1), failureB1);
            expect(selectFarthest(failureB0, failureA0), failureA0);
            expect(selectFarthest(failureB1, failureA0), failureB1);
            expect(parser, null);
          });
          test('farthest failure and joined', () {
            final parser = parsers.toChoiceParser(failureJoiner: selectFarthestJoined);
            expect(selectFarthestJoined(failureA0, failureB1), failureB1);
            expect(selectFarthestJoined(failureB1, failureA0), failureB1);
            expect(selectFarthestJoined(failureA0, failureB0).message, 'A0 OR B0');
            expect(selectFarthestJoined(failureB0, failureA0).message, 'B0 OR A0');
            expect(selectFarthestJoined(failureA1, failureB1).message, 'A1 OR B1');
            expect(selectFarthestJoined(failureB1, failureA1).message, 'B1 OR A1');
            expect(parser, null);
          });
        });
      });
      group('not', () {
        test('default', () {
          final parser = char('a').not('not "a" expected');
          isParseFailure(parser, 'a', message: 'not "a" expected');
        });
        test('neg', () {
          final parser = digit().neg('no digit expected');
          isParseFailure(parser, 'q', message: 'no digit expected');
        });
      });
      group('optional', () {
        test('without default', () {
          final parser = char('a').optional();
          expect(parser, null);
        });
        test('with default', () {
          final parser = char('a').optionalWith('0');
          expect(parser, null);
        });
      });
      group('sequence', () {
        test('operator', () {
          final parser = char('a') & char('b');
          expect(parser, null);
        });
        test('converter', () {
          final parser = [char('a'), char('b')].toSequenceParser();
          expect(parser, null);
        });
        test('two', () {
          final parser = char('a').seq(char('b'));
          expect(parser, null);
        });
        test('three', () {
          final parser = char('a').seq(char('b')).seq(char('c'));
          expect(parser, null);
        });
      });
      group('settable', () {
        test('default', () {
          final parser = char('a').settable();
          expect(parser, null);
        });
        test('undefined', () {
          final parser = undefined();
          expect(parser, null);
          parser.set(char('a'));
          expect(parser, null);
        });
      });
      group('skip', () {
        final inner = digit();
        group('none', () {
          final parser = inner.skip();
          test('default', () {
            expect(parser == inner, null); // same(inner)
            expect(parser, null);
          });
        });
        group('before', () {
          final parser = inner.skip(before: char('*'));
          test('default', () {
            expect(parser, null);
          });
        });
        group('after', () {
          final parser = inner.skip(after: char('!'));
          test('default', () {
            expect(parser, null);
          });
        });
        group('before & after', () {
          final parser = inner.skip(before: char('*'), after: char('!'));
          test('default', () {
            expect(parser, null);
          });
        });
      });
    });
    group('misc', () {
      group('end', () {
        test('default', () {
          final parser = char('a').end();
          expect(parser, null);
        });
      });
      group('epsilon', () {
        test('default', () {
          final parser = epsilon();
          expect(parser, null);
        });
      });
      group('failure', () {
        test('default', () {
          final parser = failure('failure');
          expect(parser, null);
        });
      });
      group('labeled', () {
        test('default', () {
          final parser = char('*').labeled('asterisk');
          expect(parser, null);
        });
      });
      group('newline', () {
        test('default', () {
          final parser = newline();
          expect(parser, null);
        });
      });
      group('position', () {
        test('default', () {
          final parser = (any().star() & position()).pick(-1);
          expect(parser, null);
        });
      });
    });
    group('predicate', () {
      group('any', () {
        test('default', () {
          final parser = any();
          expect(parser, null);
        });
      });
      group('pattern', () {
        test('string', () {
          final parser = PatternParser('42', 'number expected');
          expect(parser, null);
        });
        test('regexp', () {
          final parser = PatternParser(RegExp(r'\d+'), 'digits expected');
          expect(parser, null);
        });
        test('regexp groups', () {
          final parser = PatternParser(RegExp(r'(\d+)\s*,\s*(\d+)'), 'pair expected');
          expect(parser, null);
        });
      });
      group('string', () {
        test('default', () {
          final parser = string('foo');
          expect(parser, null);
        });
        test('convert empty', () {
          final parser = ''.toParser();
          expect(parser, null);
        });
        test('convert single char', () {
          final parser = 'a'.toParser();
          expect(parser, null);
        });
        test('convert single char (case-insensitive)', () {
          final parser = 'a'.toParser(caseInsensitive: true);
          expect(parser, null);
        });
        test('convert pattern', () {
          final parser = 'a-z'.toParser(isPattern: true);
          expect(parser, null);
        });
        test('convert pattern (case-insensitive)', () {
          final parser = 'a-z'.toParser(isPattern: true, caseInsensitive: true);
          expect(parser, null);
        });
        test('convert multiple chars', () {
          final parser = 'foo'.toParser();
          expect(parser, null);
        });
        test('convert multiple chars (case-insensitive)', () {
          final parser = 'foo'.toParser(caseInsensitive: true);
          expect(parser, null);
        });
      });
      group('stringIgnoreCase', () {
        test('default', () {
          final parser = stringIgnoreCase('foo');
          expect(parser, null);
        });
      });
    });
    group('repeater', () {
      group('greedy', () {
        test('star', () {
          final parser = word().starGreedy(digit());
          expect(parser, null);
        });
        test('plus', () {
          final parser = word().plusGreedy(digit());
          expect(parser, null);
        });
        test('repeat', () {
          final parser = word().repeatGreedy(digit(), 2, 4);
          expect(parser, null);
        });
        test('repeat unbounded', () {
          final inputLetter = List.filled(100000, 'a');
          final inputDigit = List.filled(100000, '1');
          final parser = word().repeatGreedy(digit(), 2, unbounded);
          expect(parser, null);
        });
      });
      group('lazy', () {
        test('star', () {
          final parser = word().starLazy(digit());
          expect(parser, null);
        });
        test('plus', () {
          final parser = word().plusLazy(digit());
          expect(parser, null);
        });
        test('repeat', () {
          final parser = word().repeatLazy(digit(), 2, 4);
          expect(parser, null);
        });
        test('repeat unbounded', () {
          final input = List.filled(100000, 'a');
          final parser = word().repeatLazy(digit(), 2, unbounded);
          expect(parser, null);
        });
      });
      group('possessive', () {
        test('star', () {
          final parser = char('a').star();
          expect(parser, null);
        });
        test('plus', () {
          final parser = char('a').plus();
          expect(parser, null);
        });
        test('repeat', () {
          final parser = char('a').repeat(2, 3);
          expect(parser, null);
        });
        test('repeat exact', () {
          final parser = char('a').repeat(2);
          expect(parser, null);
        });
        test('repeat unbounded', () {
          final input = List.filled(100000, 'a');
          final parser = char('a').repeat(2, unbounded);
          expect(parser, null);
        });
        test('repeat erroneous, 此处应该为X', () {
          char('a').repeat(-1, 1);
        });
        test('times', () {
          final parser = char('a').times(2);
          expect(parser, null);
        });
      });
      group('separated', () {
        test('star', () {
          final parser = digit().starSeparated(letter());
          expect(parser, null);
        });
        test('plus', () {
          final parser = digit().plusSeparated(letter());
          expect(parser, null);
        });
        test('times', () {
          final parser = digit().timesSeparated(letter(), 3);
          expect(parser, null);
        });
        test('repeat', () {
          final parser = digit().repeatSeparated(letter(), 2, 3);
          expect(parser, null);
        });
        group('separated list', () {
          final empty = SeparatedList<String, String>([], []);
          final single = SeparatedList<String, String>(['1'], []);
          final double = SeparatedList<String, String>(['1', '2'], ['+']);
          final triple = SeparatedList<String, String>(['1', '2', '3'], ['+', '-']);
          final quadruple = SeparatedList<String, String>(['1', '2', '3', '4'], ['+', '-', '*']);
          final mixed = SeparatedList<int, String>([1, 2, 3], ['+', '-']);
          String combinator(String first, String separator, String second) => '($first$separator$second)';
          test('elements', () {
            expect(empty.elements, []);
            expect(single.elements, ['1']);
            expect(double.elements, ['1', '2']);
            expect(triple.elements, ['1', '2', '3']);
            expect(quadruple.elements, ['1', '2', '3', '4']);
            expect(mixed.elements, [1, 2, 3]);
          });
          test('separators', () {
            expect(empty.separators, []);
            expect(single.separators, []);
            expect(double.separators, ['+']);
            expect(triple.separators, ['+', '-']);
            expect(quadruple.separators, ['+', '-', '*']);
            expect(mixed.separators, ['+', '-']);
          });
          test('sequence', () {
            expect(empty.sequential, []);
            expect(single.sequential, ['1']);
            expect(double.sequential, ['1', '+', '2']);
            expect(triple.sequential, ['1', '+', '2', '-', '3']);
            expect(quadruple.sequential, ['1', '+', '2', '-', '3', '*', '4']);
            expect(mixed.sequential, [1, '+', 2, '-', 3]);
          });
          test('foldLeft, 此处应该为X', () {
            expect(single.foldLeft(combinator), '1');
            expect(double.foldLeft(combinator), '(1+2)');
            expect(triple.foldLeft(combinator), '((1+2)-3)');
            expect(quadruple.foldLeft(combinator), '(((1+2)-3)*4)');
            empty.foldLeft(combinator);
          });
          test('foldRight, 此处应该为X', () {
            expect(single.foldRight(combinator), '1');
            expect(double.foldRight(combinator), '(1+2)');
            expect(triple.foldRight(combinator), '(1+(2-3))');
            expect(quadruple.foldRight(combinator), '(1+(2-(3*4)))');
            empty.foldRight(combinator);
          });
          test('toString', () {
            expect(empty.toString(), 'SeparatedList()');
            expect(single.toString(), 'SeparatedList(1)');
            expect(double.toString(), 'SeparatedList(1, +, 2)');
            expect(triple.toString(), 'SeparatedList(1, +, 2, -, 3)');
            expect(quadruple.toString(), 'SeparatedList(1, +, 2, -, 3, *, 4)');
            expect(mixed.toString(), 'SeparatedList(1, +, 2, -, 3)');
          });
        });
      });
      group('separated by', () {
        group('include separators', () {
          test('default', () {
            final parser = char('a').separatedBy(char('b'));
            expect(parser, null);
          });
          test('optional separator at start', () {
            final parser = char('a').separatedBy(char('b'), optionalSeparatorAtStart: true);
            expect(parser, null);
          });
          test('optional separator at end', () {
            final parser = char('a').separatedBy(char('b'), optionalSeparatorAtEnd: true);
            expect(parser, null);
          });
          test('optional separators at start and end', () {
            final parser = char('a').separatedBy(char('b'), optionalSeparatorAtStart: true, optionalSeparatorAtEnd: true);
            expect(parser, null);
          });
        });
        group('exclude separators', () {
          test('default', () {
            final parser = char('a').separatedBy(char('b'), includeSeparators: false);
            expect(parser, null);
          });
          test('optional separator at start', () {
            final parser = char('a').separatedBy(char('b'), includeSeparators: false, optionalSeparatorAtStart: true);
            expect(parser, null);
          });
          test('optional separator at end', () {
            final parser = char('a').separatedBy(char('b'), includeSeparators: false, optionalSeparatorAtEnd: true);
            expect(parser, null);
          });
          test('optional separators at start and end', () {
            final parser = char('a')
                .separatedBy(char('b'), includeSeparators: false, optionalSeparatorAtEnd: true, optionalSeparatorAtStart: true);
            expect(parser, null);
          });
        });
      });
    });
  }
}
